(** A somewhat higher-level interface to OpenGL. *)
open Gl;;

type texture = int;;
type bufferHint = int;;
type floatBuffer = (float, Bigarray.float32_elt, Bigarray.c_layout) Bigarray.Array1.t;;
type shortBuffer = (int, Bigarray.int16_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t;;

type bufferData = floatBuffer;;
type primitives = Count of int | Indices of shortBuffer;;

(** An array of some kind of OpenGL data -- like a vertex buffer
    or a color buffer. *)
type glBuffer = 
{
  data : bufferData ;     (** A bigarray containing the data *)
  elements : int;         (** The number of components in each element.
			      A 3D vertex has 3 elements. *)
  usageHint : bufferHint; (** Tells the video card how this
			      data will be used, for optimal memory
			      usage *)
};;

(** A chunk of primitives; either an index array or just a count. *)
type glPrimitives =
{
  kind : int;          (** OpenGL constant indication the primitive type (GL_TRIANGLES, GL_QUADS...) *)
  prim : primitives;   (** Short buffer or just an int *)
};;

(** Describes the current state of OpenGL *)
type renderState = 
{
  vertices  : glBuffer option;      
  colors    : glBuffer option;
  texcoords : glBuffer array option;
  textures  : texture array option;
};;

(** A geometryChunk is a renderState and a list of primitives *)
type geometryChunk = 
{
  state : renderState;
  primitives : glPrimitives list;
};;

(** Convert an array of floats to a bigarray *)
let toFloatArray a =
  Bigarray.Array1.of_array Bigarray.float32 Bigarray.c_layout a;;


let makePrimitive ~kind:k ~prim:p =
  { kind = k; prim = p; };;

let makeBuffer ~data:d ~elements:e =
  { data = d;
    elements = e; usageHint = 0; };;

let makeGeometryChunk ~state:s ~primitives:p = 
  { state = s; primitives = p };;

let makeRenderState ~vertices:v =
  { vertices = Some v; 
    colors = None; texcoords = None; textures = None; };;

let submit prim = 
  match prim.prim with
      Count num -> glDrawArrays prim.kind 0 num;
    | Indices buf -> glDrawElements prim.kind (Bigarray.Array1.dim buf) cgl_unsigned_short buf;;

let bindVertex v =
  match v with
      None ->
	glDisableClientState cgl_vertex_array
    | Some buf -> 
	glEnableClientState cgl_vertex_array;
	glVertexPointer buf.elements cgl_float 0 buf.data;;

let bindColor v =
  match v with
      None -> 
	glDisableClientState cgl_color_array
    | Some buf -> 
	glEnableClientState cgl_color_array;
	glColorPointer buf.elements cgl_float 0 buf.data;;

let loadMatrix mat mode =
  glMatrixMode mode;
  glLoadMatrixf mat;;

let multMatrix mat mode =
  glMatrixMode mode;
  glPushMatrix () ;
  glMultMatrixf mat;;

let popMatrix mode = 
  glMatrixMode mode;
  glPopMatrix ();;

let bindState s =
  bindColor s.colors;
  bindVertex s.vertices;;


let renderChunk c = 
  bindState c.state;
  List.iter submit c.primitives;;
    
(*	Array.iteri (fun index a -> 
		 glClientActiveTexture index;
		 glTexCoordPointer a.elements cgl_float 0 a.data)
    
    s.texcoord;;*)
